{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Creating data folder...\n",
      "Downloading data from https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/cifar-10-binary.tar.gz (162.2 MB)\n",
      "\n",
      "file_sizes: 100%|████████████████████████████| 170M/170M [00:02<00:00, 57.0MB/s]\n",
      "Extracting tar.gz file...\n",
      "Successfully downloaded / unzipped to ./datasets-cifar10-bin\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'./datasets-cifar10-bin'"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from download import download\n",
    "\n",
    "url = \"https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/cifar-10-binary.tar.gz\"\n",
    "\n",
    "download(url, \"./datasets-cifar10-bin\", kind=\"tar.gz\", replace=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import mindspore as ms\n",
    "import mindspore.dataset as ds\n",
    "import mindspore.dataset.vision as vision\n",
    "import mindspore.dataset.transforms as transforms\n",
    "from mindspore import dtype as mstype\n",
    "\n",
    "data_dir = \"./datasets-cifar10-bin/cifar-10-batches-bin\"  # 数据集根目录\n",
    "batch_size = 256  # 批量大小\n",
    "image_size = 32  # 训练图像空间大小\n",
    "workers = 4  # 并行线程个数\n",
    "num_classes = 10  # 分类数量\n",
    "\n",
    "\n",
    "def create_dataset_cifar10(dataset_dir, usage, resize, batch_size, workers):\n",
    "\n",
    "    data_set = ds.Cifar10Dataset(dataset_dir=dataset_dir,\n",
    "                                 usage=usage,\n",
    "                                 num_parallel_workers=workers,\n",
    "                                 shuffle=True)\n",
    "\n",
    "    trans = []\n",
    "    if usage == \"train\":\n",
    "        trans += [\n",
    "            vision.RandomCrop((32, 32), (4, 4, 4, 4)),\n",
    "            vision.RandomHorizontalFlip(prob=0.5)\n",
    "        ]\n",
    "\n",
    "    trans += [\n",
    "        vision.Resize(resize),\n",
    "        vision.Rescale(1.0 / 255.0, 0.0),\n",
    "        vision.Normalize([0.4914, 0.4822, 0.4465], [0.2023, 0.1994, 0.2010]),\n",
    "        vision.HWC2CHW()\n",
    "    ]\n",
    "\n",
    "    target_trans = transforms.TypeCast(mstype.int32)\n",
    "\n",
    "    # 数据映射操作\n",
    "    data_set = data_set.map(operations=trans,\n",
    "                            input_columns='image',\n",
    "                            num_parallel_workers=workers)\n",
    "\n",
    "    data_set = data_set.map(operations=target_trans,\n",
    "                            input_columns='label',\n",
    "                            num_parallel_workers=workers)\n",
    "\n",
    "    # 批量操作\n",
    "    data_set = data_set.batch(batch_size)\n",
    "\n",
    "    return data_set\n",
    "\n",
    "\n",
    "# 获取处理后的训练与测试数据集\n",
    "\n",
    "dataset_train = create_dataset_cifar10(dataset_dir=data_dir,\n",
    "                                       usage=\"train\",\n",
    "                                       resize=image_size,\n",
    "                                       batch_size=batch_size,\n",
    "                                       workers=workers)\n",
    "step_size_train = dataset_train.get_dataset_size()\n",
    "\n",
    "dataset_val = create_dataset_cifar10(dataset_dir=data_dir,\n",
    "                                     usage=\"test\",\n",
    "                                     resize=image_size,\n",
    "                                     batch_size=batch_size,\n",
    "                                     workers=workers)\n",
    "step_size_val = dataset_val.get_dataset_size()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Matplotlib is building the font cache; this may take a moment.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Image shape: (256, 3, 32, 32), Label shape: (256,)\n",
      "Labels: [2 0 6 2 5 6]\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAgMAAAGFCAYAAABg2vAPAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAABw4UlEQVR4nO29eZRmVXnv/5zzzlPVW/PQQ1V39UQzdCODCMigERSVS5agLhNDG40oKpKL3hj9RfHKClkOFw1GjfnlgkR/uUi8DrkRFBJEFJCxm4Zump6qqruqq2t86613Hs75/eGlV5793UAJPXq+n7WyzN487xn22We/u97n29/H8X3fF0IIIYQEFvdYXwAhhBBCji3cDBBCCCEBh5sBQgghJOBwM0AIIYQEHG4GCCGEkIDDzQAhhBAScLgZIIQQQgIONwOEEEJIwOFmgBBCCAk43AwY3HjjjeI4jkxPT79k3ODgoGzatOlVneuiiy6Siy666FUdgwSHX/ziF+I4jvziF784IY5LyO/CY489Jueee66kUilxHEc2b958rC8pUISP9QUQQggJNvV6Xa666iqJx+Nyyy23SDKZlIGBgWN9WYGCm4FXyI4dO8R1+cMKOXpccMEFUi6XJRqNHutLIeSwsnv3bhkZGZF/+Id/kA984APH+nICCb/NXiGxWEwikchLxhSLxaN0NSQIuK4r8Xj8ZTehpVLpKF0RIYeHyclJERHJZrMvGcc19cjBzcCLMD09Le985zulpaVFOjo65OMf/7hUKpVD/93UDNx+++3iOI488MADcu2110p3d7csXbr00H//9re/LUNDQ5JIJOTss8+WBx988GjeDjmOGRkZkWuvvVbWrl0riURCOjo65KqrrpLh4WEVZ8vtX3TRRXLKKafIE088IRdccIEkk0n59Kc/LSK/naNve9vb5Oc//7ls3LhR4vG4rF+/Xv73//7fL3tNDz74oFx11VWyfPlyicVismzZMvnzP/9zKZfLKm7Tpk2STqdlbGxMrrjiCkmn09LV1SWf+MQnpNlsqljP8+SrX/2qnHzyyRKPx6Wnp0euueYamZube2UDR34v2LRpk1x44YUiInLVVVeJ4zhy0UUXHZpbu3fvlssuu0wymYz80R/9kYj8dlNwww03yLJlyyQWi8natWvly1/+sphFeMvlslx33XXS2dkpmUxGLr/8chkbGxPHceTGG2882rd6XMM0wYvwzne+UwYHB+Xmm2+WRx55RP72b/9W5ubm5I477njJz1177bXS1dUln/3sZw/tYv/xH/9RrrnmGjn33HPl+uuvlz179sjll18u7e3tsmzZsqNxO+Q45rHHHpOHHnpI3v3ud8vSpUtleHhYvvnNb8pFF10k27Ztk2Qy+ZKfn5mZkbe85S3y7ne/W/74j/9Yenp6Dv23nTt3yrve9S750Ic+JFdffbXcdtttctVVV8k999wjb3rTm170mHfddZeUSiX58Ic/LB0dHfLoo4/KrbfeKvv375e77rpLxTabTbn00kvlta99rXz5y1+W++67T77yla/I0NCQfPjDHz4Ud80118jtt98u73vf++S6666TvXv3yte//nV56qmn5Ne//vXL/tJGfj+55pprZMmSJfLXf/3Xct1118lZZ50lPT098r3vfU8ajYZceumlcv7558uXv/xlSSaT4vu+XH755XL//ffL+9//ftm4caP87Gc/k09+8pMyNjYmt9xyy6Fjb9q0Sb7//e/Le9/7XjnnnHPkgQcekLe+9a3H8G6PY3yi+NznPueLiH/55Zer/muvvdYXEX/Lli2+7/v+wMCAf/XVVx/677fddpsvIv7555/vNxqNQ/21Ws3v7u72N27c6Fer1UP93/72t30R8S+88MIjej/k+KdUKkHfww8/7IuIf8cddxzqu//++30R8e+///5DfRdeeKEvIv63vvUtOMbAwIAvIv4PfvCDQ33z8/N+X1+ff/rpp7/kcW3XdPPNN/uO4/gjIyOH+q6++mpfRPz//t//u4o9/fTT/TPOOONQ+8EHH/RFxP/e976n4u655x5rPwkWL8zBu+6661DfC3PrU5/6lIr90Y9+5IuIf9NNN6n+K6+80nccx9+1a5fv+77/xBNP+CLiX3/99Spu06ZNvoj4n/vc547MzZygME3wInzkIx9R7Y997GMiIvLTn/70JT/3Z3/2ZxIKhQ61H3/8cZmcnJQPfehDSvi1adMmaW1tPYxXTE5UEonEof+/Xq/LzMyMrFq1SrLZrDz55JMv+/lYLCbve9/7rP+tv79f/vAP//BQu6WlRf7kT/5EnnrqKZmYmFjUNRWLRZmenpZzzz1XfN+Xp556CuI/9KEPqfbrX/962bNnz6H2XXfdJa2trfKmN71JpqenD/3fGWecIel0Wu6///6XvU8STP7zr0siv12DQ6GQXHfddar/hhtuEN/35e677xYRkXvuuUdEfvtr7X/mhbWcaJgmeBFWr16t2kNDQ+K6LuRxTVasWKHaIyMj1uNFIhFZuXLlq79QcsJTLpfl5ptvlttuu03GxsZU3nN+fv5lP79kyZIX/RcGq1atEsdxVN+aNWtERGR4eFh6e3utnxsdHZXPfvaz8pOf/ARy+uY1xeNx6erqUn1tbW3qczt37pT5+Xnp7u62nu8FARkh/5lwOKy0VyK/XVP7+/slk8mo/pNOOunQf3/hf13XhTV51apVR/CKT1y4GVgk5oL6Yvznv6gIWQwf+9jH5LbbbpPrr79eXve610lra6s4jiPvfve7xfO8l/384Z5zzWZT3vSmN8ns7Kz8xV/8haxbt05SqZSMjY3Jpk2b4Jr+8y9hL4bnedLd3S3f+973rP/d3EwQIvLbX734T7iPDtwMvAg7d+5UO8pdu3aJ53kyODj4Ox3nBeOMnTt3yhve8IZD/fV6Xfbu3SsbNmw4LNdLTlz+5V/+Ra6++mr5yle+cqivUqlILpd71cfetWuX+L6vNrPPP/+8iMiLzuWtW7fK888/L9/5znfkT/7kTw7133vvva/4OoaGhuS+++6T8847jxtm8qoYGBiQ++67TxYWFtSvA88999yh//7C/3qeJ3v37lW/zO7atevoXvAJArdcL8Lf/d3fqfatt94qIiJvectbfqfjnHnmmdLV1SXf+ta3pFarHeq//fbbD8tiT058QqEQ/JOoW2+9Ff5p3ithfHxcfvjDHx5q5/N5ueOOO2Tjxo0vmiJ44S/9/3xNvu/L1772tVd8He985zul2WzKF77wBfhvjUaD7wJZNJdddpk0m035+te/rvpvueUWcRzn0Bp96aWXiojIN77xDRX3wlpONPxl4EXYu3evXH755fLmN79ZHn74Yfnud78r73nPe37nv+QjkYjcdNNNcs0118gb3vAGede73iV79+6V2267jZoBIiIib3vb2+Sf/umfpLW1VdavXy8PP/yw3HfffdLR0fGqj71mzRp5//vfL4899pj09PTI//yf/1MOHjwot91224t+Zt26dTI0NCSf+MQnZGxsTFpaWuQHP/jBq/IDuPDCC+Waa66Rm2++WTZv3iyXXHKJRCIR2blzp9x1113yta99Ta688spXfHwSHN7+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M0AIIYQEHG4GCCGEkIDj+KZKihBCCCGBgr8MEEIIIQGHmwFCCCEk4HAzQAghhAQcbgYIIYSQgMPNACGEEBJwuBkghBBCAg43A4QQQkjA4WaAEEIICTjcDBBCCCEBh5sBQgghJOBwM0AIIYQEHG4GCCGEkIDDzQAhhBAScLgZIIQQQgIONwOEEEJIwOFmgBBCCAk43AwQQgghAYebAUIIISTgcDNACCGEBBxuBgghhJCAw80AIYQQEnC4GSCEEEICDjcDhBBCSMDhZoAQQggJONwMEEIIIQGHmwFCCCEk4HAzQAghhAQcbgYIIYSQgMPNACGEEBJwuBkghBBCAg43A4QQQkjA4WaAEEIICTjcDBBCCCEBh5sBQgghJOBwM2Bw4403iuM4Mj09/ZJxg4ODsmnTpld1rosuukguuuiiV3UMQhbDC/OakOOVxx57TM4991xJpVLiOI5s3rz5WF9SoAgf6wsghBASbOr1ulx11VUSj8fllltukWQyKQMDA8f6sgIFNwOvkB07dojr8ocVQgh5tezevVtGRkbkH/7hH+QDH/jAsb6cQMJvs1dILBaTSCTykjHFYvEoXQ0hhJy4TE5OiohINpt9yTiuqUcObgZehOnpaXnnO98pLS0t0tHRIR//+MelUqkc+u+mZuD2228Xx3HkgQcekGuvvVa6u7tl6dKlh/77t7/9bRkaGpJEIiFnn322PPjgg0fzdkiA+NWvfiVnnXWWxONxGRoakr//+7+HmEajIV/4whdkaGhIYrGYDA4Oyqc//WmpVqsqzvM8ufHGG6W/v1+SyaRcfPHFsm3btsOimSFERGTTpk1y4YUXiojIVVddJY7jyEUXXSSbNm2SdDotu3fvlssuu0wymYz80R/9kYj8dlNwww03yLJlyyQWi8natWvly1/+svi+r45dLpfluuuuk87OTslkMnL55ZfL2NiYOI4jN95449G+1eMapglehHe+850yODgoN998szzyyCPyt3/7tzI3Nyd33HHHS37u2muvla6uLvnsZz97aBf7j//4j3LNNdfIueeeK9dff73s2bNHLr/8cmlvb5dly5YdjdshAWHr1q1yySWXSFdXl9x4443SaDTkc5/7nPT09Ki4D3zgA/Kd73xHrrzySrnhhhvkN7/5jdx8882yfft2+eEPf3go7i//8i/li1/8orz97W+XSy+9VLZs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",
      "text/plain": [
       "<Figure size 640x480 with 6 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "data_iter = next(dataset_train.create_dict_iterator())\n",
    "\n",
    "images = data_iter[\"image\"].asnumpy()\n",
    "labels = data_iter[\"label\"].asnumpy()\n",
    "print(f\"Image shape: {images.shape}, Label shape: {labels.shape}\")\n",
    "\n",
    "# 训练数据集中，前六张图片所对应的标签\n",
    "print(f\"Labels: {labels[:6]}\")\n",
    "\n",
    "classes = []\n",
    "\n",
    "with open(data_dir + \"/batches.meta.txt\", \"r\") as f:\n",
    "    for line in f:\n",
    "        line = line.rstrip()\n",
    "        if line:\n",
    "            classes.append(line)\n",
    "\n",
    "# 训练数据集的前六张图片\n",
    "plt.figure()\n",
    "for i in range(6):\n",
    "    plt.subplot(2, 3, i + 1)\n",
    "    image_trans = np.transpose(images[i], (1, 2, 0))\n",
    "    mean = np.array([0.4914, 0.4822, 0.4465])\n",
    "    std = np.array([0.2023, 0.1994, 0.2010])\n",
    "    image_trans = std * image_trans + mean\n",
    "    image_trans = np.clip(image_trans, 0, 1)\n",
    "    plt.title(f\"{classes[labels[i]]}\")\n",
    "    plt.imshow(image_trans)\n",
    "    plt.axis(\"off\")\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Type, Union, List, Optional\n",
    "import mindspore.nn as nn\n",
    "from mindspore.common.initializer import Normal\n",
    "\n",
    "# 初始化卷积层与BatchNorm的参数\n",
    "weight_init = Normal(mean=0, sigma=0.02)\n",
    "gamma_init = Normal(mean=1, sigma=0.02)\n",
    "\n",
    "class ResidualBlockBase(nn.Cell):\n",
    "    expansion: int = 1  # 最后一个卷积核数量与第一个卷积核数量相等\n",
    "\n",
    "    def __init__(self, in_channel: int, out_channel: int,\n",
    "                 stride: int = 1, norm: Optional[nn.Cell] = None,\n",
    "                 down_sample: Optional[nn.Cell] = None) -> None:\n",
    "        super(ResidualBlockBase, self).__init__()\n",
    "        if not norm:\n",
    "            self.norm = nn.BatchNorm2d(out_channel)\n",
    "        else:\n",
    "            self.norm = norm\n",
    "\n",
    "        self.conv1 = nn.Conv2d(in_channel, out_channel,\n",
    "                               kernel_size=3, stride=stride,\n",
    "                               weight_init=weight_init)\n",
    "        self.conv2 = nn.Conv2d(in_channel, out_channel,\n",
    "                               kernel_size=3, weight_init=weight_init)\n",
    "        self.relu = nn.ReLU()\n",
    "        self.down_sample = down_sample\n",
    "\n",
    "    def construct(self, x):\n",
    "        \"\"\"ResidualBlockBase construct.\"\"\"\n",
    "        identity = x  # shortcuts分支\n",
    "\n",
    "        out = self.conv1(x)  # 主分支第一层：3*3卷积层\n",
    "        out = self.norm(out)\n",
    "        out = self.relu(out)\n",
    "        out = self.conv2(out)  # 主分支第二层：3*3卷积层\n",
    "        out = self.norm(out)\n",
    "\n",
    "        if self.down_sample is not None:\n",
    "            identity = self.down_sample(x)\n",
    "        out += identity  # 输出为主分支与shortcuts之和\n",
    "        out = self.relu(out)\n",
    "\n",
    "        return out\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ResidualBlock(nn.Cell):\n",
    "    expansion = 4  # 最后一个卷积核的数量是第一个卷积核数量的4倍\n",
    "\n",
    "    def __init__(self, in_channel: int, out_channel: int,\n",
    "                 stride: int = 1, down_sample: Optional[nn.Cell] = None) -> None:\n",
    "        super(ResidualBlock, self).__init__()\n",
    "\n",
    "        self.conv1 = nn.Conv2d(in_channel, out_channel,\n",
    "                               kernel_size=1, weight_init=weight_init)\n",
    "        self.norm1 = nn.BatchNorm2d(out_channel)\n",
    "        self.conv2 = nn.Conv2d(out_channel, out_channel,\n",
    "                               kernel_size=3, stride=stride,\n",
    "                               weight_init=weight_init)\n",
    "        self.norm2 = nn.BatchNorm2d(out_channel)\n",
    "        self.conv3 = nn.Conv2d(out_channel, out_channel * self.expansion,\n",
    "                               kernel_size=1, weight_init=weight_init)\n",
    "        self.norm3 = nn.BatchNorm2d(out_channel * self.expansion)\n",
    "\n",
    "        self.relu = nn.ReLU()\n",
    "        self.down_sample = down_sample\n",
    "\n",
    "    def construct(self, x):\n",
    "\n",
    "        identity = x  # shortscuts分支\n",
    "\n",
    "        out = self.conv1(x)  # 主分支第一层：1*1卷积层\n",
    "        out = self.norm1(out)\n",
    "        out = self.relu(out)\n",
    "        out = self.conv2(out)  # 主分支第二层：3*3卷积层\n",
    "        out = self.norm2(out)\n",
    "        out = self.relu(out)\n",
    "        out = self.conv3(out)  # 主分支第三层：1*1卷积层\n",
    "        out = self.norm3(out)\n",
    "\n",
    "        if self.down_sample is not None:\n",
    "            identity = self.down_sample(x)\n",
    "\n",
    "        out += identity  # 输出为主分支与shortcuts之和\n",
    "        out = self.relu(out)\n",
    "\n",
    "        return out\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def make_layer(last_out_channel, block: Type[Union[ResidualBlockBase, ResidualBlock]],\n",
    "               channel: int, block_nums: int, stride: int = 1):\n",
    "    down_sample = None  # shortcuts分支\n",
    "\n",
    "    if stride != 1 or last_out_channel != channel * block.expansion:\n",
    "\n",
    "        down_sample = nn.SequentialCell([\n",
    "            nn.Conv2d(last_out_channel, channel * block.expansion,\n",
    "                      kernel_size=1, stride=stride, weight_init=weight_init),\n",
    "            nn.BatchNorm2d(channel * block.expansion, gamma_init=gamma_init)\n",
    "        ])\n",
    "\n",
    "    layers = []\n",
    "    layers.append(block(last_out_channel, channel, stride=stride, down_sample=down_sample))\n",
    "\n",
    "    in_channel = channel * block.expansion\n",
    "    # 堆叠残差网络\n",
    "    for _ in range(1, block_nums):\n",
    "\n",
    "        layers.append(block(in_channel, channel))\n",
    "\n",
    "    return nn.SequentialCell(layers)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "from mindspore import load_checkpoint, load_param_into_net\n",
    "\n",
    "\n",
    "class ResNet(nn.Cell):\n",
    "    def __init__(self, block: Type[Union[ResidualBlockBase, ResidualBlock]],\n",
    "                 layer_nums: List[int], num_classes: int, input_channel: int) -> None:\n",
    "        super(ResNet, self).__init__()\n",
    "\n",
    "        self.relu = nn.ReLU()\n",
    "        # 第一个卷积层，输入channel为3（彩色图像），输出channel为64\n",
    "        self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, weight_init=weight_init)\n",
    "        self.norm = nn.BatchNorm2d(64)\n",
    "        # 最大池化层，缩小图片的尺寸\n",
    "        self.max_pool = nn.MaxPool2d(kernel_size=3, stride=2, pad_mode='same')\n",
    "        # 各个残差网络结构块定义\n",
    "        self.layer1 = make_layer(64, block, 64, layer_nums[0])\n",
    "        self.layer2 = make_layer(64 * block.expansion, block, 128, layer_nums[1], stride=2)\n",
    "        self.layer3 = make_layer(128 * block.expansion, block, 256, layer_nums[2], stride=2)\n",
    "        self.layer4 = make_layer(256 * block.expansion, block, 512, layer_nums[3], stride=2)\n",
    "        # 平均池化层\n",
    "        self.avg_pool = nn.AvgPool2d()\n",
    "        # flattern层\n",
    "        self.flatten = nn.Flatten()\n",
    "        # 全连接层\n",
    "        self.fc = nn.Dense(in_channels=input_channel, out_channels=num_classes)\n",
    "\n",
    "    def construct(self, x):\n",
    "\n",
    "        x = self.conv1(x)\n",
    "        x = self.norm(x)\n",
    "        x = self.relu(x)\n",
    "        x = self.max_pool(x)\n",
    "\n",
    "        x = self.layer1(x)\n",
    "        x = self.layer2(x)\n",
    "        x = self.layer3(x)\n",
    "        x = self.layer4(x)\n",
    "\n",
    "        x = self.avg_pool(x)\n",
    "        x = self.flatten(x)\n",
    "        x = self.fc(x)\n",
    "\n",
    "        return x\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "def _resnet(model_url: str, block: Type[Union[ResidualBlockBase, ResidualBlock]],\n",
    "            layers: List[int], num_classes: int, pretrained: bool, pretrained_ckpt: str,\n",
    "            input_channel: int):\n",
    "    model = ResNet(block, layers, num_classes, input_channel)\n",
    "\n",
    "    if pretrained:\n",
    "        # 加载预训练模型\n",
    "        download(url=model_url, path=pretrained_ckpt, replace=True)\n",
    "        param_dict = load_checkpoint(pretrained_ckpt)\n",
    "        load_param_into_net(model, param_dict)\n",
    "\n",
    "    return model\n",
    "\n",
    "\n",
    "def resnet50(num_classes: int = 1000, pretrained: bool = False):\n",
    "    \"\"\"ResNet50模型\"\"\"\n",
    "    resnet50_url = \"https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/models/application/resnet50_224_new.ckpt\"\n",
    "    resnet50_ckpt = \"./LoadPretrainedModel/resnet50_224_new.ckpt\"\n",
    "    return _resnet(resnet50_url, ResidualBlock, [3, 4, 6, 3], num_classes,\n",
    "                   pretrained, resnet50_ckpt, 2048)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading data from https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/models/application/resnet50_224_new.ckpt (97.7 MB)\n",
      "\n",
      "file_sizes: 100%|████████████████████████████| 102M/102M [00:02<00:00, 40.4MB/s]\n",
      "Successfully downloaded file to ./LoadPretrainedModel/resnet50_224_new.ckpt\n"
     ]
    }
   ],
   "source": [
    "# 定义ResNet50网络\n",
    "network = resnet50(pretrained=True)\n",
    "\n",
    "# 全连接层输入层的大小\n",
    "in_channel = network.fc.in_channels\n",
    "fc = nn.Dense(in_channels=in_channel, out_channels=10)\n",
    "# 重置全连接层\n",
    "network.fc = fc\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 设置学习率\n",
    "num_epochs = 5\n",
    "lr = nn.cosine_decay_lr(min_lr=0.00001, max_lr=0.001, total_step=step_size_train * num_epochs,\n",
    "                        step_per_epoch=step_size_train, decay_epoch=num_epochs)\n",
    "# 定义优化器和损失函数\n",
    "opt = nn.Momentum(params=network.trainable_params(), learning_rate=lr, momentum=0.9)\n",
    "loss_fn = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')\n",
    "\n",
    "\n",
    "def forward_fn(inputs, targets):\n",
    "    logits = network(inputs)\n",
    "    loss = loss_fn(logits, targets)\n",
    "    return loss\n",
    "\n",
    "\n",
    "grad_fn = ms.value_and_grad(forward_fn, None, opt.parameters)\n",
    "\n",
    "\n",
    "def train_step(inputs, targets):\n",
    "    loss, grads = grad_fn(inputs, targets)\n",
    "    opt(grads)\n",
    "    return loss\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "# 创建迭代器\n",
    "data_loader_train = dataset_train.create_tuple_iterator(num_epochs=num_epochs)\n",
    "data_loader_val = dataset_val.create_tuple_iterator(num_epochs=num_epochs)\n",
    "\n",
    "# 最佳模型存储路径\n",
    "best_acc = 0\n",
    "best_ckpt_dir = \"./BestCheckpoint\"\n",
    "best_ckpt_path = \"./BestCheckpoint/resnet50-best.ckpt\"\n",
    "\n",
    "if not os.path.exists(best_ckpt_dir):\n",
    "    os.mkdir(best_ckpt_dir)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "import mindspore.ops as ops\n",
    "\n",
    "\n",
    "def train(data_loader, epoch):\n",
    "    \"\"\"模型训练\"\"\"\n",
    "    losses = []\n",
    "    network.set_train(True)\n",
    "\n",
    "    for i, (images, labels) in enumerate(data_loader):\n",
    "        loss = train_step(images, labels)\n",
    "        if i % 100 == 0 or i == step_size_train - 1:\n",
    "            print('Epoch: [%3d/%3d], Steps: [%3d/%3d], Train Loss: [%5.3f]' %\n",
    "                  (epoch + 1, num_epochs, i + 1, step_size_train, loss))\n",
    "        losses.append(loss)\n",
    "\n",
    "    return sum(losses) / len(losses)\n",
    "\n",
    "\n",
    "def evaluate(data_loader):\n",
    "    \"\"\"模型验证\"\"\"\n",
    "    network.set_train(False)\n",
    "\n",
    "    correct_num = 0.0  # 预测正确个数\n",
    "    total_num = 0.0  # 预测总数\n",
    "\n",
    "    for images, labels in data_loader:\n",
    "        logits = network(images)\n",
    "        pred = logits.argmax(axis=1)  # 预测结果\n",
    "        correct = ops.equal(pred, labels).reshape((-1, ))\n",
    "        correct_num += correct.sum().asnumpy()\n",
    "        total_num += correct.shape[0]\n",
    "\n",
    "    acc = correct_num / total_num  # 准确率\n",
    "\n",
    "    return acc\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Start Training Loop ...\n",
      "Epoch: [  1/  5], Steps: [  1/196], Train Loss: [2.415]\n"
     ]
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[15], line 5\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mStart Training Loop ...\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m      4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m epoch \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(num_epochs):\n\u001b[0;32m----> 5\u001b[0m     curr_loss \u001b[38;5;241m=\u001b[39m \u001b[43mtrain\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdata_loader_train\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mepoch\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m      6\u001b[0m     curr_acc \u001b[38;5;241m=\u001b[39m evaluate(data_loader_val)\n\u001b[1;32m      8\u001b[0m     \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m-\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m*\u001b[39m \u001b[38;5;241m50\u001b[39m)\n",
      "Cell \u001b[0;32mIn[14], line 10\u001b[0m, in \u001b[0;36mtrain\u001b[0;34m(data_loader, epoch)\u001b[0m\n\u001b[1;32m      7\u001b[0m network\u001b[38;5;241m.\u001b[39mset_train(\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[1;32m      9\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i, (images, labels) \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(data_loader):\n\u001b[0;32m---> 10\u001b[0m     loss \u001b[38;5;241m=\u001b[39m \u001b[43mtrain_step\u001b[49m\u001b[43m(\u001b[49m\u001b[43mimages\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mlabels\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     11\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m i \u001b[38;5;241m%\u001b[39m \u001b[38;5;241m100\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m \u001b[38;5;129;01mor\u001b[39;00m i \u001b[38;5;241m==\u001b[39m step_size_train \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[1;32m     12\u001b[0m         \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mEpoch: [\u001b[39m\u001b[38;5;132;01m%3d\u001b[39;00m\u001b[38;5;124m/\u001b[39m\u001b[38;5;132;01m%3d\u001b[39;00m\u001b[38;5;124m], Steps: [\u001b[39m\u001b[38;5;132;01m%3d\u001b[39;00m\u001b[38;5;124m/\u001b[39m\u001b[38;5;132;01m%3d\u001b[39;00m\u001b[38;5;124m], Train Loss: [\u001b[39m\u001b[38;5;132;01m%5.3f\u001b[39;00m\u001b[38;5;124m]\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;241m%\u001b[39m\n\u001b[1;32m     13\u001b[0m               (epoch \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m, num_epochs, i \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m, step_size_train, loss))\n",
      "Cell \u001b[0;32mIn[12], line 20\u001b[0m, in \u001b[0;36mtrain_step\u001b[0;34m(inputs, targets)\u001b[0m\n\u001b[1;32m     19\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mtrain_step\u001b[39m(inputs, targets):\n\u001b[0;32m---> 20\u001b[0m     loss, grads \u001b[38;5;241m=\u001b[39m \u001b[43mgrad_fn\u001b[49m\u001b[43m(\u001b[49m\u001b[43minputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtargets\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     21\u001b[0m     opt(grads)\n\u001b[1;32m     22\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m loss\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/ops/composite/base.py:625\u001b[0m, in \u001b[0;36m_Grad.__call__.<locals>.after_grad\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m    624\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mafter_grad\u001b[39m(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[0;32m--> 625\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mgrad_\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfn_\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mweights\u001b[49m\u001b[43m)\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/common/api.py:121\u001b[0m, in \u001b[0;36m_wrap_func.<locals>.wrapper\u001b[0;34m(*arg, **kwargs)\u001b[0m\n\u001b[1;32m    119\u001b[0m \u001b[38;5;129m@wraps\u001b[39m(fn)\n\u001b[1;32m    120\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mwrapper\u001b[39m(\u001b[38;5;241m*\u001b[39marg, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[0;32m--> 121\u001b[0m     results \u001b[38;5;241m=\u001b[39m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43marg\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    122\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m _convert_python_data(results)\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/ops/composite/base.py:600\u001b[0m, in \u001b[0;36m_Grad.__call__.<locals>.after_grad\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m    598\u001b[0m \u001b[38;5;129m@_wrap_func\u001b[39m\n\u001b[1;32m    599\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mafter_grad\u001b[39m(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[0;32m--> 600\u001b[0m     res \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pynative_forward_run\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mgrad_\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mweights\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    601\u001b[0m     _pynative_executor\u001b[38;5;241m.\u001b[39mgrad(fn, grad_, weights, grad_position, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m    602\u001b[0m     out \u001b[38;5;241m=\u001b[39m _pynative_executor()\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/ops/composite/base.py:650\u001b[0m, in \u001b[0;36m_Grad._pynative_forward_run\u001b[0;34m(self, fn, grad, weights, args, kwargs)\u001b[0m\n\u001b[1;32m    648\u001b[0m _pynative_executor\u001b[38;5;241m.\u001b[39mset_grad_flag(\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[1;32m    649\u001b[0m _pynative_executor\u001b[38;5;241m.\u001b[39mnew_graph(fn, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mnew_kwargs)\n\u001b[0;32m--> 650\u001b[0m outputs \u001b[38;5;241m=\u001b[39m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mnew_kwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    651\u001b[0m _pynative_executor\u001b[38;5;241m.\u001b[39mend_graph(fn, outputs, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mnew_kwargs)\n\u001b[1;32m    652\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m outputs\n",
      "Cell \u001b[0;32mIn[12], line 11\u001b[0m, in \u001b[0;36mforward_fn\u001b[0;34m(inputs, targets)\u001b[0m\n\u001b[1;32m     10\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mforward_fn\u001b[39m(inputs, targets):\n\u001b[0;32m---> 11\u001b[0m     logits \u001b[38;5;241m=\u001b[39m \u001b[43mnetwork\u001b[49m\u001b[43m(\u001b[49m\u001b[43minputs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     12\u001b[0m     loss \u001b[38;5;241m=\u001b[39m loss_fn(logits, targets)\n\u001b[1;32m     13\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m loss\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/nn/cell.py:701\u001b[0m, in \u001b[0;36mCell.__call__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m    699\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m    700\u001b[0m     _pynative_executor\u001b[38;5;241m.\u001b[39mnew_graph(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m--> 701\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_run_construct\u001b[49m\u001b[43m(\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    702\u001b[0m     _pynative_executor\u001b[38;5;241m.\u001b[39mend_graph(\u001b[38;5;28mself\u001b[39m, output, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m    703\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m err:\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/nn/cell.py:482\u001b[0m, in \u001b[0;36mCell._run_construct\u001b[0;34m(self, cast_inputs, kwargs)\u001b[0m\n\u001b[1;32m    480\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_shard_fn(\u001b[38;5;241m*\u001b[39mcast_inputs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m    481\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 482\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mconstruct\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mcast_inputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    483\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_enable_forward_hook:\n\u001b[1;32m    484\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_run_forward_hook(cast_inputs, output)\n",
      "Cell \u001b[0;32mIn[9], line 35\u001b[0m, in \u001b[0;36mResNet.construct\u001b[0;34m(self, x)\u001b[0m\n\u001b[1;32m     32\u001b[0m x \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmax_pool(x)\n\u001b[1;32m     34\u001b[0m x \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlayer1(x)\n\u001b[0;32m---> 35\u001b[0m x \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlayer2\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     36\u001b[0m x \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlayer3(x)\n\u001b[1;32m     37\u001b[0m x \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlayer4(x)\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/nn/cell.py:701\u001b[0m, in \u001b[0;36mCell.__call__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m    699\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m    700\u001b[0m     _pynative_executor\u001b[38;5;241m.\u001b[39mnew_graph(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m--> 701\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_run_construct\u001b[49m\u001b[43m(\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    702\u001b[0m     _pynative_executor\u001b[38;5;241m.\u001b[39mend_graph(\u001b[38;5;28mself\u001b[39m, output, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m    703\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m err:\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/nn/cell.py:482\u001b[0m, in \u001b[0;36mCell._run_construct\u001b[0;34m(self, cast_inputs, kwargs)\u001b[0m\n\u001b[1;32m    480\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_shard_fn(\u001b[38;5;241m*\u001b[39mcast_inputs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m    481\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 482\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mconstruct\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mcast_inputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    483\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_enable_forward_hook:\n\u001b[1;32m    484\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_run_forward_hook(cast_inputs, output)\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/nn/layer/container.py:295\u001b[0m, in \u001b[0;36mSequentialCell.construct\u001b[0;34m(self, input_data)\u001b[0m\n\u001b[1;32m    293\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mconstruct\u001b[39m(\u001b[38;5;28mself\u001b[39m, input_data):\n\u001b[1;32m    294\u001b[0m     \u001b[38;5;28;01mfor\u001b[39;00m cell \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcell_list:\n\u001b[0;32m--> 295\u001b[0m         input_data \u001b[38;5;241m=\u001b[39m \u001b[43mcell\u001b[49m\u001b[43m(\u001b[49m\u001b[43minput_data\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    296\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m input_data\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/nn/cell.py:701\u001b[0m, in \u001b[0;36mCell.__call__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m    699\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m    700\u001b[0m     _pynative_executor\u001b[38;5;241m.\u001b[39mnew_graph(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m--> 701\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_run_construct\u001b[49m\u001b[43m(\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    702\u001b[0m     _pynative_executor\u001b[38;5;241m.\u001b[39mend_graph(\u001b[38;5;28mself\u001b[39m, output, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m    703\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m err:\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/nn/cell.py:482\u001b[0m, in \u001b[0;36mCell._run_construct\u001b[0;34m(self, cast_inputs, kwargs)\u001b[0m\n\u001b[1;32m    480\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_shard_fn(\u001b[38;5;241m*\u001b[39mcast_inputs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m    481\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 482\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mconstruct\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mcast_inputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    483\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_enable_forward_hook:\n\u001b[1;32m    484\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_run_forward_hook(cast_inputs, output)\n",
      "Cell \u001b[0;32mIn[7], line 27\u001b[0m, in \u001b[0;36mResidualBlock.construct\u001b[0;34m(self, x)\u001b[0m\n\u001b[1;32m     24\u001b[0m identity \u001b[38;5;241m=\u001b[39m x  \u001b[38;5;66;03m# shortscuts分支\u001b[39;00m\n\u001b[1;32m     26\u001b[0m out \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mconv1(x)  \u001b[38;5;66;03m# 主分支第一层：1*1卷积层\u001b[39;00m\n\u001b[0;32m---> 27\u001b[0m out \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mnorm1\u001b[49m\u001b[43m(\u001b[49m\u001b[43mout\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     28\u001b[0m out \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mrelu(out)\n\u001b[1;32m     29\u001b[0m out \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mconv2(out)  \u001b[38;5;66;03m# 主分支第二层：3*3卷积层\u001b[39;00m\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/nn/cell.py:701\u001b[0m, in \u001b[0;36mCell.__call__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m    699\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m    700\u001b[0m     _pynative_executor\u001b[38;5;241m.\u001b[39mnew_graph(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m--> 701\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_run_construct\u001b[49m\u001b[43m(\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    702\u001b[0m     _pynative_executor\u001b[38;5;241m.\u001b[39mend_graph(\u001b[38;5;28mself\u001b[39m, output, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m    703\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m err:\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/nn/cell.py:482\u001b[0m, in \u001b[0;36mCell._run_construct\u001b[0;34m(self, cast_inputs, kwargs)\u001b[0m\n\u001b[1;32m    480\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_shard_fn(\u001b[38;5;241m*\u001b[39mcast_inputs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m    481\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 482\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mconstruct\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mcast_inputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    483\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_enable_forward_hook:\n\u001b[1;32m    484\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_run_forward_hook(cast_inputs, output)\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/nn/layer/normalization.py:142\u001b[0m, in \u001b[0;36m_BatchNorm.construct\u001b[0;34m(self, x)\u001b[0m\n\u001b[1;32m    140\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39muse_batch_statistics \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m    141\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mtraining:\n\u001b[0;32m--> 142\u001b[0m         \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbn_train\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    143\u001b[0m \u001b[43m                             \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgamma\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    144\u001b[0m \u001b[43m                             \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbeta\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    145\u001b[0m \u001b[43m                             \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmoving_mean\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    146\u001b[0m \u001b[43m                             \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmoving_variance\u001b[49m\u001b[43m)\u001b[49m[\u001b[38;5;241m0\u001b[39m]\n\u001b[1;32m    147\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mtraining:\n\u001b[1;32m    148\u001b[0m         \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mbn_infer(x,\n\u001b[1;32m    149\u001b[0m                              \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mgamma,\n\u001b[1;32m    150\u001b[0m                              \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mbeta,\n\u001b[1;32m    151\u001b[0m                              \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmoving_mean,\n\u001b[1;32m    152\u001b[0m                              \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmoving_variance)[\u001b[38;5;241m0\u001b[39m]\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/ops/primitive.py:314\u001b[0m, in \u001b[0;36mPrimitive.__call__\u001b[0;34m(self, *args)\u001b[0m\n\u001b[1;32m    312\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m should_elim:\n\u001b[1;32m    313\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m output\n\u001b[0;32m--> 314\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_run_op\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/ops/primitive.py:913\u001b[0m, in \u001b[0;36m_run_op\u001b[0;34m(obj, op_name, args)\u001b[0m\n\u001b[1;32m    911\u001b[0m         \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(arg, Parameter) \u001b[38;5;129;01mand\u001b[39;00m arg\u001b[38;5;241m.\u001b[39mhas_init:\n\u001b[1;32m    912\u001b[0m             arg\u001b[38;5;241m.\u001b[39minit_data()\n\u001b[0;32m--> 913\u001b[0m     stub \u001b[38;5;241m=\u001b[39m \u001b[43m_pynative_executor\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun_op_async\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mop_name\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    914\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m _convert_stub(stub)\n\u001b[1;32m    915\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m _RunOpHook\u001b[38;5;241m.\u001b[39mcurrent\u001b[38;5;241m.\u001b[39mhook(obj, args)\n",
      "File \u001b[0;32m~/py39_mindspore/lib/python3.9/site-packages/mindspore/common/api.py:1186\u001b[0m, in \u001b[0;36m_PyNativeExecutor.run_op_async\u001b[0;34m(self, *args)\u001b[0m\n\u001b[1;32m   1176\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mrun_op_async\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs):\n\u001b[1;32m   1177\u001b[0m \u001b[38;5;250m    \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m   1178\u001b[0m \u001b[38;5;124;03m    Run single op async.\u001b[39;00m\n\u001b[1;32m   1179\u001b[0m \n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m   1184\u001b[0m \u001b[38;5;124;03m        StubNode, result of run op.\u001b[39;00m\n\u001b[1;32m   1185\u001b[0m \u001b[38;5;124;03m    \"\"\"\u001b[39;00m\n\u001b[0;32m-> 1186\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_executor\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun_op_async\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
     ]
    }
   ],
   "source": [
    "# 开始循环训练\n",
    "print(\"Start Training Loop ...\")\n",
    "\n",
    "for epoch in range(num_epochs):\n",
    "    curr_loss = train(data_loader_train, epoch)\n",
    "    curr_acc = evaluate(data_loader_val)\n",
    "\n",
    "    print(\"-\" * 50)\n",
    "    print(\"Epoch: [%3d/%3d], Average Train Loss: [%5.3f], Accuracy: [%5.3f]\" % (\n",
    "        epoch+1, num_epochs, curr_loss, curr_acc\n",
    "    ))\n",
    "    print(\"-\" * 50)\n",
    "\n",
    "    # 保存当前预测准确率最高的模型\n",
    "    if curr_acc > best_acc:\n",
    "        best_acc = curr_acc\n",
    "        ms.save_checkpoint(network, best_ckpt_path)\n",
    "\n",
    "print(\"=\" * 80)\n",
    "print(f\"End of validation the best Accuracy is: {best_acc: 5.3f}, \"\n",
    "      f\"save the best ckpt file in {best_ckpt_path}\", flush=True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "\n",
    "def visualize_model(best_ckpt_path, dataset_val):\n",
    "    num_class = 10  # 对狼和狗图像进行二分类\n",
    "    net = resnet50(num_class)\n",
    "    # 加载模型参数\n",
    "    param_dict = ms.load_checkpoint(best_ckpt_path)\n",
    "    ms.load_param_into_net(net, param_dict)\n",
    "    # 加载验证集的数据进行验证\n",
    "    data = next(dataset_val.create_dict_iterator())\n",
    "    images = data[\"image\"]\n",
    "    labels = data[\"label\"]\n",
    "    # 预测图像类别\n",
    "    output = net(data['image'])\n",
    "    pred = np.argmax(output.asnumpy(), axis=1)\n",
    "\n",
    "    # 图像分类\n",
    "    classes = []\n",
    "\n",
    "    with open(data_dir + \"/batches.meta.txt\", \"r\") as f:\n",
    "        for line in f:\n",
    "            line = line.rstrip()\n",
    "            if line:\n",
    "                classes.append(line)\n",
    "\n",
    "    # 显示图像及图像的预测值\n",
    "    plt.figure()\n",
    "    for i in range(6):\n",
    "        plt.subplot(2, 3, i + 1)\n",
    "        # 若预测正确，显示为蓝色；若预测错误，显示为红色\n",
    "        color = 'blue' if pred[i] == labels.asnumpy()[i] else 'red'\n",
    "        plt.title('predict:{}'.format(classes[pred[i]]), color=color)\n",
    "        picture_show = np.transpose(images.asnumpy()[i], (1, 2, 0))\n",
    "        mean = np.array([0.4914, 0.4822, 0.4465])\n",
    "        std = np.array([0.2023, 0.1994, 0.2010])\n",
    "        picture_show = std * picture_show + mean\n",
    "        picture_show = np.clip(picture_show, 0, 1)\n",
    "        plt.imshow(picture_show)\n",
    "        plt.axis('off')\n",
    "\n",
    "    plt.show()\n",
    "\n",
    "\n",
    "# 使用测试数据集进行验证\n",
    "visualize_model(best_ckpt_path=best_ckpt_path, dataset_val=dataset_val)\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.19"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
